package org.example.java_gobang.game;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.java_gobang.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

//创建匹配器,用来匹配玩家
@Component
public class Matcher {
    @Autowired
    private OnlineUserManager onlineUserManager;

    private ObjectMapper objectMapper = new ObjectMapper();
    //创建3个匹配队列:
    //<2000的进入到normQueue
    //2000-3000的进入highQueue
    //>3000的进入veryHighQueue队列
    //普通玩家匹配队列
    private Queue<User> normQueue = new LinkedList<>();
    //高级玩家匹配队列
    private Queue<User> highQueue = new LinkedList<>();
    //非常高级玩家匹配队列
    private Queue<User> veryHighQueue = new LinkedList<>();

    @Autowired
    private RoomManager roomManager;


    //玩家点击开始匹配后,匹配入队列
    public void add(User user){
        if(user.getScore()<2000){
            synchronized (normQueue){
                normQueue.offer(user);
                //当往队列中加入元素后,就要去尝试唤醒可能因队列中无元素而被阻塞的线程,让其重新尝试获取锁
                normQueue.notify();
            }
            System.out.println("把玩家 "+user.getUsername()+" 加入normQueue");
        }else if(user.getScore()>=2000 && user.getScore()<3000){
            synchronized (highQueue){
                highQueue.offer(user);
                //当往队列中加入元素后,就要去尝试唤醒可能因队列中无元素而被阻塞的线程,让其重新尝试获取锁
                highQueue.notify();
            }
            System.out.println("把玩家 "+user.getUsername()+" 加入highQueue");
        }else{
            synchronized(veryHighQueue){
                veryHighQueue.offer(user);
                //当往队列中加入元素后,就要去尝试唤醒可能因队列中无元素而被阻塞的线程,让其重新尝试获取锁
                veryHighQueue.notify();
            };
            System.out.println("把玩家 "+user.getUsername()+" 加入veryHighQueue");
        }
    }

    //玩家点击停止匹配后,玩家出队列
    public void remove(User user){
        if(user.getScore()<2000){
            synchronized (normQueue){
                normQueue.remove(user);
            }
            System.out.println("把玩家 "+user.getUsername()+" 移除normlQueue");
        }else if(user.getScore()>=2000 && user.getScore()<3000){
            synchronized (highQueue){
                highQueue.remove(user);
            }
            System.out.println("把玩家 "+user.getUsername()+" 移除highQueue");
        }else{
            synchronized (veryHighQueue){
                veryHighQueue.remove(user);
            }
            System.out.println("把玩家 "+user.getUsername()+" 移除veryHighQueue");
        }
    }

    //创建3个线程分别扫描队列，查看匹配结果:
    public Matcher(){
        Thread t1 = new Thread(){
            @Override
            public void run() {
                while(true){
                    HandlerMatch(normQueue);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while(true){
                    HandlerMatch(highQueue);
                }
            }
        };
        t2.start();

         Thread t3 = new Thread(){
            @Override
            public void run() {
                while(true){
                    HandlerMatch(veryHighQueue);
                }
            }
        };
        t3.start();

    }

    //根据传进来的队列 进行玩家匹配:
    private void HandlerMatch(Queue<User> queue) {
        synchronized(queue){
            try{
                // 1. 当队列中的玩家个数<2时,无法匹配,让其进行阻塞等待
                // 并不断 判断玩家个数是否>=2,使用while巡护,不要用if,让其忙等的获取 返回
                while(queue.size()<2){
                    queue.wait();
//                    return;
                }
                // 2 . 从队列中获取2个玩家,进行游戏对战
                User player1 = queue.poll();
                User player2 = queue.poll();
                System.out.println("匹配到两个玩家: "+player1.getUsername()+" , "+player2.getUsername());
                //2.1先获取当前玩家的信息,用于检验和返回信息
                WebSocketSession session1 = onlineUserManager.getFromGameHall(player1.getUserId());
                WebSocketSession session2 = onlineUserManager.getFromGameHall(player2.getUserId());
                //2.2 玩家信息校验:
                //校验1: 当异常情况,一方玩家不存在时,将另一方玩家放回到匹配队列中
                //这种情况正常情况下,不会发生,因为前面已经校验过了,这里校验为了增加稳定性
                if(session1==null){
                    queue.offer(player2);
                    return;
                }
                if(session2==null){
                    queue.offer(player1);
                    return;
                }
                //校验2: 当匹配到了两个玩家时同一个用户:将一个玩家放回匹配队列
                //这种情况,在非极端异常的情况下,也是不会发生的
                //因为前面已经设置过,当玩家下线没救将其移除匹配队列 和 禁止玩家多开
                //这里也是加重判断
                if(session1 == session2){
                    queue.offer(player1);
                    return;
                }
                //2.3 将匹配好的两个玩家放到一个游戏房间中
                //创建一个游戏房间
                Room room = new Room();
                //加入到房间管理器中:
                roomManager.add(room,player1.getUserId(),player2.getUserId());

                //2.4 将相关信息返回给两个用户
                //你匹配到对手了
                MatchResponse response1 = new MatchResponse();
                response1.setOk(true);
                response1.setMessage("matchSuccess");
                //将json格式转换成string格式
                String s1 = objectMapper.writeValueAsString(response1);
                session1.sendMessage(new TextMessage(s1));

                MatchResponse response2 = new MatchResponse();
                response2.setOk(true);
                response2.setMessage("matchSuccess");
                //将json格式转换成string格式,
                String s2 = objectMapper.writeValueAsString(response2);
                session2.sendMessage(new TextMessage(s2));

            }catch(IOException | InterruptedException e ){
                e.printStackTrace();
            }
        }

    }
}
